<template>
  <div id="flyLineBox"></div>
  <div id="flyLineBtn">
    <el-row>
      <el-button type="info" @click="addFlyline(this.flyString.style1)">
        样式一
      </el-button>
    </el-row>
    <el-row>
      <el-button type="info" @click="addFlyline(this.flyString.style2)">
        样式二
      </el-button>
    </el-row>
    <el-row>
      <el-button type="info" @click="addFlyline(this.flyString.style3)">
        样式三
      </el-button>
    </el-row>
    <el-row>
      <el-button type="info" @click="addFlyline(this.flyString.style4)">
        样式四
      </el-button>
    </el-row>
  </div>
</template>

<script>
import InitCesium from "@/lib/initCesium.js";
import "cesium/Build/Cesium/Widgets/widgets.css";
import * as Cesium from "cesium";

export default {
  data() {
    return {
      viewerObj: null,
      flyLine: null,
      flyString: {
        style1: `         
                        varying vec2 v_st;    
                        varying float v_width;    
                        varying float v_polylineAngle;
                        varying vec4 v_positionEC;
                        varying vec3 v_normalEC;
                        void main()
                        {
                            vec2 st = v_st;

                            float xx = fract(st.s - czm_frameNumber/60.0);
                            float r = xx;
                            float g = 0.0;
                            float b = 0.0;
                            float a = xx;

                            gl_FragColor = vec4(r,g,b,a);
                        }

                `,
        style2: `
                        varying vec2 v_st;    
                        varying float v_width;    
                        varying float v_polylineAngle;
                        varying vec4 v_positionEC;
                        varying vec3 v_normalEC;
                        void main()
                        {
                            vec2 st = v_st;

                            // 七彩渐变飞线，宽度2
                            float xx = fract(st.s*2.0 - czm_frameNumber/60.0);
                            float r = xx;
                            float g = sin(czm_frameNumber/30.0);
                            float b = cos(czm_frameNumber/30.0);
                            float a = xx;

                            gl_FragColor = vec4(r,g,b,a);
                        }
                `,
        style3: `
                        varying vec2 v_st;    
                        varying float v_width;    
                        varying float v_polylineAngle;
                        varying vec4 v_positionEC;
                        varying vec3 v_normalEC;
                        void main()
                        {
                            vec2 st = v_st;
                            
                            //卡巴斯基
                            float xx = sin(st.s*6.0 -czm_frameNumber/5.0) - cos(st.t*6.0);
                            float r = 0.0;
                            float g = xx;
                            float b = xx;
                            float a = xx;

                            gl_FragColor = vec4(r,g,b,a);
                        }
                `,
        style4: `
                        varying vec2 v_st;    
                        varying float v_width;    
                        varying float v_polylineAngle;
                        varying vec4 v_positionEC;
                        varying vec3 v_normalEC;
                        void main()
                        {
                            vec2 st = v_st;
                            // 箭头飞线，宽度 8
                            float xx = fract(st.s*10.0 + st.t  - czm_frameNumber/60.0);
                            if (st.t<0.5) {
                                xx = fract(st.s*10.0 - st.t - czm_frameNumber/60.0);
                            }
                            float r = 0.0;
                            float g = xx;
                            float b = xx;
                            float a = xx;

                            // 飞线边框
                            if (st.t>0.8||st.t<0.2) {
                                g = 1.0;
                                b = 1.0;
                                a = 0.4;
                            }

                            gl_FragColor = vec4(r,g,b,a);
                        }
                `,
      },
    };
  },
  mounted() {
    this.init();
  },
  unmounted() {
    this.viewerObj.destroy();
  },
  methods: {
    init() {
      this.viewerObj = InitCesium.init("flyLineBox");

      this.viewerObj.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(
          124.323256,
          42.863033,
          250000.0
        ),
        orientation: {
          heading: Cesium.Math.toRadians(45),
          pitch: Cesium.Math.toRadians(-60),
          roll: 0.0,
        },
        duration: 2,
      });

      this.addFlyline(this.flyString.style1);
    },
    computeFlyline(
      point1 = [126.323256, 42.863033],
      point2 = [124.323256, 44.863033],
      h = 100000
    ) {
      var flyline = getBSRxyz(...point1, ...point2, h);
      return flyline;

      function getBSRxyz(x1, y1, x2, y2, h) {
        var arr3d = getBSRPoints(x1, y1, x2, y2, h);
        var arrAll = [];
        for (var ite of arr3d) {
          arrAll.push(ite[0]);
          arrAll.push(ite[1]);
          arrAll.push(ite[2]);
        }
        return Cesium.Cartesian3.fromDegreesArrayHeights(arrAll);
      }

      function getBSRPoints(x1, y1, x2, y2, h) {
        var point1 = [y1, 0];
        var point2 = [(y2 + y1) / 2, h];
        var point3 = [y2, 0];
        var arr = getBSR(point1, point2, point3);
        var arr3d = [];
        for (var i = 0; i < arr.length; i++) {
          var x = ((x2 - x1) * (arr[i][0] - y1)) / (y2 - y1) + x1;
          arr3d.push([x, arr[i][0], arr[i][1]]);
        }
        return arr3d;
      }

      function getBSR(point1, point2, point3) {
        var ps = [
          { x: point1[0], y: point1[1] },
          { x: point2[0], y: point2[1] },
          { x: point3[0], y: point3[1] },
        ];
        var guijipoints = CreateBezierPoints(ps, 100);
        return guijipoints;
      }

      function CreateBezierPoints(anchorpoints, pointsAmount) {
        var points = [];
        for (var i = 0; i < pointsAmount; i++) {
          var point = MultiPointBezier(anchorpoints, i / pointsAmount);
          points.push([point.x, point.y]);
        }
        return points;
      }

      function MultiPointBezier(points, t) {
        var len = points.length;
        var x = 0,
          y = 0;
        var erxiangshi = function (start, end) {
          var cs = 1,
            bcs = 1;
          while (end > 0) {
            cs *= start;
            bcs *= end;
            start--;
            end--;
          }
          return cs / bcs;
        };
        for (var i = 0; i < len; i++) {
          var point = points[i];
          x +=
            point.x *
            Math.pow(1 - t, len - 1 - i) *
            Math.pow(t, i) *
            erxiangshi(len - 1, i);
          y +=
            point.y *
            Math.pow(1 - t, len - 1 - i) *
            Math.pow(t, i) *
            erxiangshi(len - 1, i);
        }
        return { x: x, y: y };
      }
    },
    getFlylineMaterial(shaderSource) {
      var material = new Cesium.Material.fromType("Color");
      material.uniforms.color = Cesium.Color.ORANGE;
      var fragmentShaderSource = shaderSource;
      const aper = new Cesium.PolylineMaterialAppearance({
        material: material,
        translucent: true,
        vertexShaderSource: `
                        #define CLIP_POLYLINE 
                        void clipLineSegmentToNearPlane(
                            vec3 p0,
                            vec3 p1,
                            out vec4 positionWC,
                            out bool clipped,
                            out bool culledByNearPlane,
                            out vec4 clippedPositionEC)
                        {
                            culledByNearPlane = false;
                            clipped = false;
                            vec3 p0ToP1 = p1 - p0;
                            float magnitude = length(p0ToP1);
                            vec3 direction = normalize(p0ToP1);
                            float endPoint0Distance =  czm_currentFrustum.x + p0.z;
                            float denominator = -direction.z;
                            if (endPoint0Distance > 0.0 && abs(denominator) < czm_epsilon7)
                            {
                                culledByNearPlane = true;
                            }
                            else if (endPoint0Distance > 0.0)
                            {
                                float t = endPoint0Distance / denominator;
                                if (t < 0.0 || t > magnitude)
                                {
                                    culledByNearPlane = true;
                                }
                                else
                                {
                                    p0 = p0 + t * direction;
                                    p0.z = min(p0.z, -czm_currentFrustum.x);
                                    clipped = true;
                                }
                            }
                            clippedPositionEC = vec4(p0, 1.0);
                            positionWC = czm_eyeToWindowCoordinates(clippedPositionEC);
                        }
                        vec4 getPolylineWindowCoordinatesEC(vec4 positionEC, vec4 prevEC, vec4 nextEC, float expandDirection, float width, bool usePrevious, out float angle)
                        {
                            #ifdef POLYLINE_DASH
                            vec4 positionWindow = czm_eyeToWindowCoordinates(positionEC);
                            vec4 previousWindow = czm_eyeToWindowCoordinates(prevEC);
                            vec4 nextWindow = czm_eyeToWindowCoordinates(nextEC);
                            vec2 lineDir;
                            if (usePrevious) {
                                lineDir = normalize(positionWindow.xy - previousWindow.xy);
                            }
                            else {
                                lineDir = normalize(nextWindow.xy - positionWindow.xy);
                            }
                            angle = atan(lineDir.x, lineDir.y) - 1.570796327;
                            angle = floor(angle / czm_piOverFour + 0.5) * czm_piOverFour;
                            #endif
                            vec4 clippedPrevWC, clippedPrevEC;
                            bool prevSegmentClipped, prevSegmentCulled;
                            clipLineSegmentToNearPlane(prevEC.xyz, positionEC.xyz, clippedPrevWC, prevSegmentClipped, prevSegmentCulled, clippedPrevEC);
                            vec4 clippedNextWC, clippedNextEC;
                            bool nextSegmentClipped, nextSegmentCulled;
                            clipLineSegmentToNearPlane(nextEC.xyz, positionEC.xyz, clippedNextWC, nextSegmentClipped, nextSegmentCulled, clippedNextEC);
                            bool segmentClipped, segmentCulled;
                            vec4 clippedPositionWC, clippedPositionEC;
                            clipLineSegmentToNearPlane(positionEC.xyz, usePrevious ? prevEC.xyz : nextEC.xyz, clippedPositionWC, segmentClipped, segmentCulled, clippedPositionEC);
                            if (segmentCulled)
                            {
                                return vec4(0.0, 0.0, 0.0, 1.0);
                            }
                            vec2 directionToPrevWC = normalize(clippedPrevWC.xy - clippedPositionWC.xy);
                            vec2 directionToNextWC = normalize(clippedNextWC.xy - clippedPositionWC.xy);
                            if (prevSegmentCulled)
                            {
                                directionToPrevWC = -directionToNextWC;
                            }
                            else if (nextSegmentCulled)
                            {
                                directionToNextWC = -directionToPrevWC;
                            }
                            vec2 thisSegmentForwardWC, otherSegmentForwardWC;
                            if (usePrevious)
                            {
                                thisSegmentForwardWC = -directionToPrevWC;
                                otherSegmentForwardWC = directionToNextWC;
                            }
                            else
                            {
                                thisSegmentForwardWC = directionToNextWC;
                                otherSegmentForwardWC =  -directionToPrevWC;
                            }
                            vec2 thisSegmentLeftWC = vec2(-thisSegmentForwardWC.y, thisSegmentForwardWC.x);
                            vec2 leftWC = thisSegmentLeftWC;
                            float expandWidth = width * 0.5;
                            if (!czm_equalsEpsilon(prevEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1) && !czm_equalsEpsilon(nextEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1))
                            {
                                vec2 otherSegmentLeftWC = vec2(-otherSegmentForwardWC.y, otherSegmentForwardWC.x);
                                vec2 leftSumWC = thisSegmentLeftWC + otherSegmentLeftWC;
                                float leftSumLength = length(leftSumWC);
                                leftWC = leftSumLength < czm_epsilon6 ? thisSegmentLeftWC : (leftSumWC / leftSumLength);
                                vec2 u = -thisSegmentForwardWC;
                                vec2 v = leftWC;
                                float sinAngle = abs(u.x * v.y - u.y * v.x);
                                expandWidth = clamp(expandWidth / sinAngle, 0.0, width * 2.0);
                            }
                            vec2 offset = leftWC * expandDirection * expandWidth * czm_pixelRatio;
                            return vec4(clippedPositionWC.xy + offset, -clippedPositionWC.z, 1.0) * (czm_projection * clippedPositionEC).w;
                        }
                        vec4 getPolylineWindowCoordinates(vec4 position, vec4 previous, vec4 next, float expandDirection, float width, bool usePrevious, out float angle)
                        {
                            vec4 positionEC = czm_modelViewRelativeToEye * position;
                            vec4 prevEC = czm_modelViewRelativeToEye * previous;
                            vec4 nextEC = czm_modelViewRelativeToEye * next;
                            return getPolylineWindowCoordinatesEC(positionEC, prevEC, nextEC, expandDirection, width, usePrevious, angle);
                        }

                        attribute vec3 position3DHigh;
                        attribute vec3 position3DLow;
                        attribute vec3 prevPosition3DHigh;
                        attribute vec3 prevPosition3DLow;
                        attribute vec3 nextPosition3DHigh;
                        attribute vec3 nextPosition3DLow;
                        attribute vec2 expandAndWidth;
                        attribute vec2 st;
                        attribute float batchId;

                        varying float v_width;
                        varying vec2 v_st;
                        varying float v_polylineAngle;
                        
                        varying vec4 v_positionEC;
                        varying vec3 v_normalEC;
                        void main()
                        {
                        float expandDir = expandAndWidth.x;
                        float width = abs(expandAndWidth.y) + 0.5;
                        bool usePrev = expandAndWidth.y < 0.0;

                        vec4 p = czm_computePosition();
                        vec4 prev = czm_computePrevPosition();
                        vec4 next = czm_computeNextPosition();
                        
                        float angle;
                        vec4 positionWC = getPolylineWindowCoordinates(p, prev, next, expandDir, width, usePrev, angle);
                        gl_Position = czm_viewportOrthographic * positionWC;
                        
                        v_width = width;
                        v_st.s = st.s;
                        v_st.t = st.t;
                        // v_st.t = czm_writeNonPerspective(st.t, gl_Position.w);
                        v_polylineAngle = angle;


                        
                        vec4 eyePosition = czm_modelViewRelativeToEye * p;
                        v_positionEC =  czm_inverseModelView * eyePosition;      // position in eye coordinates
                        //v_normalEC = czm_normal * normal;                         // normal in eye coordinates
                        }

                    `,
        fragmentShaderSource: fragmentShaderSource,
      });
      return aper;
    },
    addFlyline(shaderSource) {
      const PolylineGeometry = new Cesium.PolylineGeometry({
        positions: this.computeFlyline(),
        width: 2,
      });
      const instance = new Cesium.GeometryInstance({
        geometry: PolylineGeometry,
        id: "flyline",
      });
      if (this.flyLine != null) {
        this.viewerObj.scene.primitives.remove(this.flyLine);
      }
      this.flyLine = this.viewerObj.scene.primitives.add(
        new Cesium.Primitive({
          geometryInstances: [instance],
          appearance: this.getFlylineMaterial(shaderSource),
          releaseGeometryInstances: false,
          compressVertices: false,
        })
      );
    },
  },
};
</script>

<style>
#flyLineBox {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

#flyLineBtn {
  position: absolute;
  top: 5px;
  right: 5px;
  padding: 15px;
  border-radius: 3px;
  background-color: #ccc;
}

#flyLineBtn button {
  width: 100px;
}

#flyLineBtn .el-row:not(:first-child) .el-button {
  margin-top: 10px;
}
</style>
